#include "GeneticsEngine.h"
#include "Track.h"
#include "Entity.h"
#include "Mutator.h"
#include "Util.h"
#include "Application.h"
#include "RenderWindow.h"
#include "MainWindow.h"
#include "kf_log.h"

GeneticsEngine::GeneticsEngine()
: m_pTrack(NULL)
, m_pFather(NULL)
, m_pMother(NULL)
, m_maxChildren(100)
, m_mutationRate(15)
, m_maxCarSpeed(100)
, m_offTrackPenalty(2)
, m_numEvolutions(0)
, m_fastestLap(1000)
, m_averageLap(0)
, m_slowestLap(0)
, m_isPaused(true)
, m_count(0)
, m_debugOutput(false)
, m_evolutionsPerRender(1)
, m_timeout(0)
, m_numGenes(160)
{
	ApplyWeights(50, 50);
}

GeneticsEngine::~GeneticsEngine()
{
}

bool GeneticsEngine::Init(Track* a_pTrack)
{
	m_pTrack = a_pTrack;
	
	std::vector<Genome> baseGenes;
	for(int i=0; i<m_numGenes; ++i)
	{
		Genome temp;
		temp.m_brakeValue = 1.0f;
		baseGenes.push_back(temp);
	}
	//Spawn the first batch of agents
	MutateAll(baseGenes);

	return true;
}

void GeneticsEngine::RunRace()
{
	int count = 0;
	while(count++ < m_evolutionsPerRender)
	{
	//Get the base genome
	std::vector<Genome> mutatedGenes = Merge(m_pFather, m_pMother);
	//Generate the new batch of children based on them
	if(!mutatedGenes.empty())
	{
		MutateAll(mutatedGenes);

		//UPDATE GUI//
		++m_numEvolutions;

		UpdateGUI();
	}

	//run lap on all agents
	for(std::vector<Entity*>::iterator iter = m_entities.begin(); iter != m_entities.end(); ++iter)
	{
		Entity* pElem = *iter;
		m_pTrack->RunLap(pElem);
	}
	m_pTrack->ResetShit();
	EndCycle();
	}
}
void GeneticsEngine::RunCycle(float a_dt)
{
	if(!m_isPaused)
	{
		m_count += a_dt;
		if(m_count >= 1.0f/10.0f)
		{
			//Update
			if( m_pTrack->HasRaceFinished())
			{
				RunRace();
			}
			else
			{
				m_pTrack->Update(a_dt);
			}
			m_count -= 1.0f/10.0f;
		}
	}
}

void GeneticsEngine::EndCycle()
{
	//Generate the parents (the 2 fittest agents)
	GenerateParents();
	if(m_debugOutput)
	{
		kf_log("Father Output. Evo#: "<<m_numEvolutions);
		std::string string;
		char buffer[64];
		for(int i=0; i<m_numGenes; ++i)
		{
			sprintf_s(buffer, 64, "%f, ", m_pFather->GetGenome(i).m_brakeValue);
			string.append(buffer);
		}
		kf_log(string);
	}
}
void GeneticsEngine::UpdateGUI()
{
	char buffer[16];
	sprintf_s(buffer, 16, "%i", m_numEvolutions);
	Application::GetInstance().GetWindow()->GetMainWindow()->GetEvolutionCount()->GetEdit()->SetText(buffer);

	sprintf_s(buffer, 16, "%i", (int)m_fastestLap);
	Application::GetInstance().GetWindow()->GetMainWindow()->GetFastestLap()->GetEdit()->SetText(buffer);

	sprintf_s(buffer, 16, "%i", (int)m_slowestLap);
	Application::GetInstance().GetWindow()->GetMainWindow()->GetSlowestLap()->GetEdit()->SetText(buffer);
}
void GeneticsEngine::GenerateParents()
{
	//Clear them first incase there was left over data from another cycle
	m_pFather = NULL;
	m_pMother = NULL;

	//Find the 2 fittest and assign them to the mother and father pointers
	for(std::vector<Entity*>::iterator iter = m_entities.begin(); iter != m_entities.end(); ++iter)
	{
		Entity* pElem = *iter;
		
		if(pElem->GetFitness() < m_fastestLap)
			m_fastestLap = pElem->GetFitness();

		if(pElem->GetFitness() > m_slowestLap)
			m_slowestLap = pElem->GetFitness();

		//If there is no father assigned yet
		if(m_pFather == NULL)
		{
			m_pFather = pElem;
		}
		//If there is no mother assigned yet
		else if(m_pMother == NULL)
		{
			m_pMother = pElem;
		}
		//If your better than the father (1st place)
		else if(pElem->GetFitness() < m_pFather->GetFitness())
		{
			//The mother becomes the old father (shift down a place)
			m_pMother = m_pFather;
			//The father becomes the new fastest
			m_pFather = pElem;
		}
		//If your better than the mother (2nd place)
		else if(pElem->GetFitness() < m_pMother->GetFitness())
		{
			//You we better than 2nd place, but not better than 1st, so you're now 2nd place
			m_pMother = pElem;
		}
	}
}

void GeneticsEngine::MutateAll(std::vector<Genome> a_mutatedGenes)
{
	//If there are old entites left over, clean them up
	if(!m_entities.empty())
	{
		for(std::vector<Entity*>::iterator iter = m_entities.begin(); iter != m_entities.end(); ++iter)
		{
			Entity* pElem = *iter;
			if(pElem)
			{
				delete pElem;
				pElem = NULL;
			}
		}
		m_entities.clear();
	}

	//Generate the entities
	for(int i=0; i<m_maxChildren; ++i)
	{
		Entity* pEntity = new Entity();
		pEntity->SetFitness(0);
		//Mutate the genes and apply to this child
		Mutate(*pEntity, a_mutatedGenes, m_mutationRate);
		m_entities.push_back(pEntity);
	}
}
void GeneticsEngine::Mutate(Entity& a_entity, std::vector<Genome> a_genome, float a_mutationRate)
{
	if(!a_genome.empty())
	{
		std::vector<Genome> genomes = a_genome;
		//mutate the genomes
		for(int i=0; i<a_mutationRate; ++i)
		{
			//Generate a new random braking value
			genomes.at((int)Util::GetRandomFloat(0, m_numGenes)).m_brakeValue = Util::GetRandomFloat(0.1, 1.0);
		}
		a_entity.SetGenome(genomes);
	}
}
std::vector<Genome> GeneticsEngine::Merge(Entity* a_pLhs, Entity* a_pRhs)
{
	std::vector<Genome> mergedGenome;

	//Make sure both parents are valid first
	if(a_pLhs && a_pRhs)
	{
		//Loop through all the genomes
		for(int i=0; i<m_numGenes; ++i)
		{
			//Calculate a new genome by getting the correct influence from each parent
			Genome genome;
			genome.m_brakeValue = (a_pLhs->GetGenome(i).m_brakeValue * m_fatherWeight) + (a_pRhs->GetGenome(i).m_brakeValue * m_motherWeight);
			mergedGenome.push_back(genome);
		}
	}

	return mergedGenome;
}
void GeneticsEngine::ApplyWeights(float a_weight1, float a_weight2)
{
	m_fatherWeight = a_weight1/100;
	m_motherWeight = a_weight2/100;
}
void ApplyMutations(Genome* a_pGenome)
{

}
void GeneticsEngine::SetMaxChildren(int a_max)
{
	m_maxChildren = a_max;
}
void GeneticsEngine::SetCarSpeed(float a_speed)
{
	m_maxCarSpeed = a_speed;
}
void GeneticsEngine::SetOffTrackPenalty(float a_penalty)
{
	m_offTrackPenalty = a_penalty;
}
void GeneticsEngine::Pause(bool a_pause)
{
	m_isPaused = a_pause;
	//UpdateGUI();
}
void GeneticsEngine::Reset()
{
	//@TODO - Finish
	m_numEvolutions = 0;
	//UpdateGUI();
}
void GeneticsEngine::EnableDebugOutput(bool a_output)
{
	m_debugOutput = a_output;
}
void GeneticsEngine::SetEvolutionsPer(int a_number)
{
	m_evolutionsPerRender = a_number;
}
void GeneticsEngine::SetTimeout(float a_timeout)
{
	m_timeout = a_timeout;
}
Entity* GeneticsEngine::GetFather() const
{
	return m_pFather;
}
Entity* GeneticsEngine::GetMother() const
{
	return m_pMother;
}
float GeneticsEngine::GetOffTrackPenalty() const
{
	return m_offTrackPenalty;
}
bool GeneticsEngine::IsPaused() const
{
	return m_isPaused;
}

Track* GeneticsEngine::GetTrack()
{
  return m_pTrack;
}